package tcp

import (
	"net"
	"operator/pkg/codes/tcp"
	"operator/pkg/format"
	"operator/pkg/network"
	"strconv"
	"sync"
	"time"
)

type NewTcpServer struct {
	mu       sync.RWMutex
	list     map[string]*network.NetStatus
	listener *net.TCPListener
	dictFn   map[string]func([]byte) []byte
	ipChan   <-chan string
	port     int
}

func (ts *NewTcpServer) dealConn(conn net.Conn) {
	buffer := make([]byte, 1024*30)
	for {
		n, err := conn.Read(buffer)
		if err != nil {
			format.Errors("pkg/network/meshnet/tcp/server.go", 28, err.Error())
			break
		}

		data := tcp.Decode(buffer[:n])
		if data.Tag().IsResponse() {

		} else {
			if fn, ok := ts.dictFn[data.Tag().String()]; ok {
				res := fn(data.Data())
				resdata := data.ResponseData(res)
				format.Println("pkg/network/meshnet/tcp/server.go", 39, "res data:\r\n", resdata.String())
				_, err = conn.Write(resdata.ToBytes())
				if err != nil {
					format.Errors("pkg/network/meshnet/tcp/server.go", 42, err.Error())
					break
				}
			}
		}
	}
}

func (ts *NewTcpServer) Write(addr, tag string, data []byte) {
	if conn, ok := ts.list[addr]; ok {
		d := tcp.Encode(tag, data)
		err := conn.Write(d.ToBytes())
		if err != nil {
			format.Errors("pkg/network/meshnet/tcp/server.go", 55, err.Error())
		}
	}
}

func (ts *NewTcpServer) BroadCast(tag string, data []byte) {
	d := tcp.Encode(tag, data)
	for _, conn := range ts.list {
		err := conn.Write(d.ToBytes())
		if err != nil {
			format.Errors("pkg/network/meshnet/tcp/server.go", 65, err.Error())
			continue
		}
	}
}

func (ts *NewTcpServer) Run(stopCh <-chan struct{}) {
	for {
		select {
		case <-stopCh:
			break
		case ip := <-ts.ipChan:
			err := ts.dealOneIp(ip)
			if err != nil {
				format.Errors("pkg/network/meshnet/tcp/server.go", 79, err.Error())
			}

		}
	}
}

func (ts *NewTcpServer) dealOneIp(ip string) error {
	var conn net.Conn
	var err error

	for {
		conn, err = net.Dial("tcp", ip+":"+strconv.Itoa(ts.port))
		if err == nil {
			break
		}
		time.Sleep(time.Second)
	}

	ts.mu.Lock()
	defer ts.mu.Unlock()

	status := network.NewNetStatus(conn, time.Second*20)
	ts.list[conn.RemoteAddr().String()] = status

	go ts.dealConn(conn)
	//go func() {
	//	for i := 0; i < 10; i++ {
	//		time.Sleep(time.Second * 3)
	//		ts.Write(conn.RemoteAddr().String(), "test", []byte{1})
	//	}
	//}()

	return nil
}

func (ts *NewTcpServer) AddFn(tag string, fn func([]byte) []byte) {
	ts.dictFn[tag] = fn
}

func NewServer(port int, ipChan <-chan string) (*NewTcpServer, error) {
	tcp := &NewTcpServer{
		list: map[string]*network.NetStatus{},
		mu:   sync.RWMutex{},
		dictFn: map[string]func([]byte) []byte{
			"hi": func(bytes []byte) []byte {
				return []byte("hi")
			},
		},
		ipChan: ipChan,
		port:   port,
	}

	return tcp, nil
}
